/**
  ******************************************************************************
  *Copyright(C),Tongji University
  * @file           : BEEP_stm32f4xx_hal.c
  * @Author			: ultralyj
  * @Date			: 2020.10.27
  * @brief          : contain the functions of beep
  ******************************************************************************
  */
#include "BEEP_stm32f4xx_hal.h"
#include "main.h"
#include "stm32f4xx_hal.h"

/* TIM3 is used to generate PWM */
extern TIM_HandleTypeDef htim3;
/* TIM4 is used to count time for tones */
extern TIM_HandleTypeDef htim4;

uint8_t Pulse_Select = 0;
uint16_t Pulse_SingalPeriod = 0, *Pulse_Song, SongNum = 0;

/* the tone table(frequency) 0-6 low pitch 7-13 middle pich 14-20 high pitch*/
const uint16_t Beep_ToneTable[21] = {
	6802, 6060, 5713, 5101, 4524, 4031, 3596,
	3400, 3029, 2700, 2543, 2267, 2019, 1798,
	1697, 1512, 1350, 1271, 1133, 1009, 898};

#if FUNMODE
const uint16_t song_Plum[] = {
	0x0602, 0x0602, 0x1304, 0x1201, 0x1101, 0x0702, 0x1102, 0x0702, 0x0601, 0x0501, 0x060B,
	0x0622, 0x1104, 0x0621, 0x1101, 0x1202, 0x1202, 0x1301, 0x1401, 0x1301, 0x1201, 0x130B,
	0x1321, 0x1501, 0x1606, 0x1501, 0x1301, 0x1206, 0x1101, 0x1201, 0x1306, 0x1201, 0x1301, 0x0608,
	0x0702, 0x0701, 0x0601, 0x0502, 0x0702, 0x0521, 0x0301, 0x1102, 0x0702, 0x060B,
	0x0641, 0x1604, 0x1502, 0x1301, 0x1501, 0x1301, 0x1501, 0x1604,
	0x1502, 0x1202, 0x1202, 0x1301, 0x1501, 0x1301, 0x1201, 0x1306,
	0x1101, 0x0601, 0x0606, 0x1301, 0x1201, 0x1304, 0x1501, 0x1601, 0x1301, 0x1201, 0x130F,
	0x0601, 0x1604, 0x1502, 0x1301, 0x1501, 0x1301, 0x1501, 0x1604,
	0x1502, 0x1202, 0x1202, 0x1301, 0x1501, 0x1301, 0x1201, 0x1306,
	0x1101, 0x0601, 0x0606, 0x1301, 0x1201, 0x1304, 0x1501, 0x1601, 0x1301, 0x1201, 0x130F,
	0x0601, 0x1604, 0x1502, 0x1301, 0x1501, 0x1301, 0x1501, 0x1604,
	0x1502, 0x1202, 0x1202, 0x1301, 0x1501, 0x1301, 0x1201, 0x1306,
	0x1101, 0x0601, 0x0606, 0x1101, 0x0601, 0x0606, 0x1301, 0x1201, 0x1306, 0x1301, 0x1201, 0x1306,
	0x1382, 0x1504, 0x0502, 0x060B};

const uint16_t song_IronFlow[] = {
	0x0603, 0x0601, 0x0608, 0x0621, 0x0301, 0x0601, 0x0701, 0x1103, 0x1101, 0x1108,
	0x1102, 0x1202, 0x0508, 0x0404, 0x0304, 0x020B,
	0x0643, 0x0601, 0x0608, 0x0601, 0x0301, 0x0601, 0x0701, 0x1103, 0x1101, 0x1108,
	0x1102, 0x1202, 0x0508, 0x0322, 0x0402, 0x0502, 0x0608, 0x0708,
	0x1103, 0x1101, 0x1108, 0x1101, 0x0601, 0x1101, 0x1301, 0x1403, 0x1401, 0x1408,
	0x1402, 0x1102, 0x1308, 0x0522, 0x0702, 0x1102, 0x1208, 0x1104, 0x0704,
	0x1103, 0x1101, 0x1108, 0x1101, 0x0601, 0x1101, 0x1301, 0x1403, 0x1401, 0x1408,
	0x1502, 0x1402, 0x1304, 0x1104, 0x0522, 0x1102, 0x1302, 0x1206, 0x1201, 0x1201, 0x1302, 0x1306,
	0x1601, 0x1601, 0x1606, 0x1601, 0x1601, 0x1606};
const uint16_t song_ZVP[] = {
	0x1202,0x1302,0x1202,0x1302,0x1102,0x0602,0x1142,0x0602,0x1342,0x0602,
	0x1242,0x1302,0x1202,0x1302,0x1102,0x0602,0x1142,0x0602,0x1342,0x0602,
	0x1242,0x1302,0x1202,0x1302,0x1102,0x0602,0x1142,0x0602,0x1342,0x0602,
	0x0742,0x1102,0x0702,0x1102,0x0602,0x0302,0x0202,0x0102,0x0202,0x0302,0x0602,0x1102,0x1202,0x1302,0x1602,0x1102
};

const uint16_t song_wind[] = {
	0x1203, 0x1101, 0x1203, 0x1101, 0x1202, 0x1302, 0x1502, 0x1301, 0x1201,
	0x1203, 0x1101, 0x1203, 0x1101, 0x1201, 0x1301, 0x1201, 0x1101, 0x0504,
	0x1203, 0x1101, 0x1203, 0x1101, 0x1202, 0x1302, 0x1502, 0x1301, 0x1201,
	0x1203, 0x1301, 0x1202, 0x1101, 0x1201, 0x1208,
	0x1243, 0x1101, 0x1203, 0x1101, 0x1202, 0x1302, 0x1502, 0x1301, 0x1201,
	0x1203, 0x1301, 0x1202, 0x1101, 0x0601, 0x0604,
	0x1301,	0x1201,	0x1101,	0x1201,	0x1104, 0x1301, 0x1201, 0x1101, 0x1201,
	0x1103,	0x0501,	0x1301,	0x1201,	0x1101, 0x1201, 0x1104,
	0x1102, 0x1202, 0x1302, 0x1102, 0x1602, 0x1501, 0x1604,
	0x1101, 0x1702, 0x1601, 0x1705, 0x1702, 0x1601, 0x1704, 0x1301,
	0x2101, 0x2201, 0x2101, 0x1701, 0x1602,
	0x1502, 0x1602, 0x1501, 0x1602, 0x1501, 0x1601, 0x1501, 0x1602, 0x1501, 0x1203,
	0x1501, 0x1301, 0x1308,
	0x1142, 0x1202, 0x1302, 0x1102, 0x1602, 0x1501, 0x1604,
	0x1101, 0x1702, 0x1601, 0x1705, 0x1702, 0x1601, 0x1704, 0x1301,
	0x2101, 0x2201, 0x2101, 0x1701, 0x1602,
	0x1502, 0x1602, 0x2301, 0x2303, 0x1502, 0x1602, 0x2301, 0x2303,
	0x1501, 0x1601, 0x160B,
	0x2142, 0x2202, 0x2302, 0x2601, 0x2503, 0x2601, 0x2503, 0x2601, 0x2503, 0x2201,
	0x2303, 0x2601, 0x2503, 0x2601, 0x2503, 0x2601, 0x2503, 0x2201, 0x2301,
	0x2202, 0x2101, 0x1603, 0x2101, 0x2101, 0x2201, 0x2101, 0x1603, 0x2103,
	0x2305, 0x2401, 0x2301, 0x2201, 0x2302, 0x2203,
	0x2102, 0x2202, 0x2302, 0x2601, 0x2503, 0x2601, 0x2503, 0x2601, 0x2503, 0x2201,
	0x2303, 0x2601, 0x2503, 0x2601, 0x2503, 0x2601, 0x2503, 0x2201, 0x2301,
	0x2202, 0x2101, 0x1603, 0x2303, 0x2201,	0x2101, 0x1603, 0x2103, 0x210B,0x0000
};
#endif
/* Private function prototypes -----------------------------------------------*/
static void HAL_BEEP_SingalTone(uint16_t period);

/**
  * @brief switch the signal TIM4 generates
  * @param None
  * @retval None
  */
void TIM4_Pulse_Select(void)
{
	switch (Pulse_Select)
	{
	case BEEP_ENABLE_SINGAL:
		HAL_BEEP_SingalTone(Pulse_SingalPeriod);
		break;
	case BEEP_ENABLE_SONG:
		HAL_BEEP_Song(Pulse_Song, SongNum);
		break;
	}
}

/**
  * @brief wait for some time and then stop the PWM(Private)
  * @param period, 0.1s as a unit
  * @retval None
  */
static void HAL_BEEP_SingalTone(uint16_t period)
{
	static uint16_t TIM4_Buff = 0;
	TIM4_Buff++;
	if (TIM4_Buff == period)
	{
		TIM4_Buff = 0;
		/* stop sending signal*/
		HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_1);
		Pulse_Select = 0;
	}
}

/**
  * @brief generate a singal tone
  * @param tone,1-7 represent do-xi
  * @param pitch -1 -> low, 0 -> middle, 1 -> high
  * @param period, 0.1s as a unit
  * @retval None
  */
void HAL_BEEP_TONE(uint8_t tone, signed char pitch, uint8_t period)
{
	if (tone == 0 || tone > 7)
		return;
	switch (pitch)
	{
	case -1:
		HAL_BEEP_Transmit(Beep_ToneTable[tone - 1]);
		break;
	case 0:
		HAL_BEEP_Transmit(Beep_ToneTable[tone + 6]);
		break;
	case 1:
		HAL_BEEP_Transmit(Beep_ToneTable[tone + 13]);
		break;
	}
	/* enable the timer4 to generate for a certain second */
	Pulse_Select = BEEP_ENABLE_SINGAL;
	Pulse_SingalPeriod = period;
}

/**
  * @brief play a song
  * @param music, a series of 16-bit data
  * for example like 0x1305
  * the first number "1" :pitch 0->low 1->middle 2->high
  * the second number "3":tone 1-7
  * the third number :wait period
  * the last numbers :period
  * @retval None
  */
void HAL_BEEP_Song(uint16_t *music, int n)
{
	static uint16_t TIM4_Buff = 0, TIM4_Buff2 = 0, p = 0, firstcall = 1, waitperiod = 0;
	uint16_t tone = 0;

	/* configure timer4 for continuously play the song */
	if (firstcall)
	{
		Pulse_Select = BEEP_ENABLE_SONG;
		Pulse_Song = music;
		SongNum = n;
		firstcall = 0;
	}
	/* wait period: to pause for a moment between tones */
	if (waitperiod == 0)
	{
		TIM4_Buff++;
		tone = music[p] % 0x1000 / 0x0100;
		if (music[p] / 0x1000 == 0)
			HAL_BEEP_Transmit(Beep_ToneTable[tone - 1]);
		if (music[p] / 0x1000 == 1)
			HAL_BEEP_Transmit(Beep_ToneTable[tone + 6]);
		if (music[p] / 0x1000 == 2)
			HAL_BEEP_Transmit(Beep_ToneTable[tone + 13]);
		/* when time achieves the period, prepare the next tone */
		if (TIM4_Buff - TIM4_Buff2-1 == music[p] % 0x0010)
		{
			p++;
			HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_1);
			/* wait for a period(0.1s) */
			waitperiod = music[p] % 0x0100 / 0x0010;
			TIM4_Buff2 = TIM4_Buff;
			/* finish the music, default the variables */
			if (p == n|| music[p] == 0)
			{
				firstcall = 1;
				p = 0;
				TIM4_Buff = 0;
				TIM4_Buff2 = 0;
				HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_1);
				Pulse_Select = 0;
				return;
			}
		}
	}
	else
		waitperiod--;
}

/**
  * @brief the basic function for beep, it will constantly generate signal once called
  * @param pvalue, the autoload value, pvalue = 1 000 000 / frequency;
  * @retval None
  */
void HAL_BEEP_Transmit(uint16_t pvalue)
{
	uint16_t buff;
	__HAL_TIM_SET_AUTORELOAD(&htim3, pvalue);
	buff = pvalue / 2;
	HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t *)&buff, 1);
}
